package com.jinwei.qunguanjia.web.system.controller;

import com.jinwei.qunguanjia.common.annotation.Log;
import com.jinwei.qunguanjia.common.constant.UserConstants;
import com.jinwei.qunguanjia.common.core.domain.Response;
import com.jinwei.qunguanjia.common.enums.BusinessTypeEnum;
import com.jinwei.qunguanjia.common.utils.JsonUtils;
import com.jinwei.qunguanjia.common.utils.StringUtils;
import com.jinwei.qunguanjia.system.dto.SysMenuDTO;
import com.jinwei.qunguanjia.system.service.SysMenuService;
import com.jinwei.qunguanjia.web.system.vo.MenuRoleTreeSelectVO;
import com.jinwei.qunguanjia.web.system.vo.MenuTreeSelectVO;
import com.jinwei.qunguanjia.web.system.vo.SysMenuVO;
import org.springframework.beans.BeanUtils;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;


/**
 * 菜单信息
 * 
 * @author ruoyi
 */
@RestController
@RequestMapping("/system/menu")
public class SysMenuController {

    @Resource
    private SysMenuService sysMenuService;

    /**
     * 新增菜单
     * @param sysMenuVO 对象
     */
    @PreAuthorize("@ss.hasPermission('system:menu:save')")
    @Log(title = "菜单管理-新增", businessType = BusinessTypeEnum.INSERT)
    @PostMapping("/save")
    public Response save(@Validated @RequestBody SysMenuVO sysMenuVO) throws IllegalAccessException {
        SysMenuDTO sysMenuDTO = new SysMenuDTO();
        BeanUtils.copyProperties(sysMenuVO, sysMenuDTO);
        if (UserConstants.NOT_UNIQUE.equals(sysMenuService.checkMenuNameUnique(sysMenuDTO))) {
            return Response.fail("新增菜单'" + sysMenuVO.getMenuName() + "'失败，菜单名称已存在");
        } else if (UserConstants.YES_FRAME.equals(sysMenuVO.getIsFrame()) && !StringUtils.ishttp(sysMenuVO.getPath())) {
            return Response.fail("新增菜单'" + sysMenuVO.getMenuName() + "'失败，地址必须以http(s)://开头");
        }
        sysMenuService.save(sysMenuDTO);
        return Response.success();
    }

    /**
     * 修改菜单
     * @param sysMenuVO 对象
     */
    @PreAuthorize("@ss.hasPermission('system:menu:update')")
    @Log(title = "菜单管理-修改", businessType = BusinessTypeEnum.UPDATE)
    @PutMapping("/update")
    public Response edit(@Validated @RequestBody SysMenuVO sysMenuVO) throws IllegalAccessException {
        SysMenuDTO sysMenuDTO = new SysMenuDTO();
        BeanUtils.copyProperties(sysMenuVO, sysMenuDTO);
        if (UserConstants.NOT_UNIQUE.equals(sysMenuService.checkMenuNameUnique(sysMenuDTO))) {
            return Response.fail("新增菜单'" + sysMenuVO.getMenuName() + "'失败，菜单名称已存在");
        } else if (UserConstants.YES_FRAME.equals(sysMenuVO.getIsFrame()) && !StringUtils.ishttp(sysMenuVO.getPath())) {
            return Response.fail("新增菜单'" + sysMenuVO.getMenuName() + "'失败，地址必须以http(s)://开头");
        } else if (sysMenuVO.getMenuId().equals(sysMenuVO.getParentId())) {
            return Response.fail("新增菜单'" + sysMenuVO.getMenuName() + "'失败，上级菜单不能选择自己");
        }
        sysMenuService.update(sysMenuDTO);
        return Response.success();
    }

    /**
     * 删除菜单
     * @param menuId 菜单id
     */
    @PreAuthorize("@ss.hasPermission('system:menu:remove')")
    @Log(title = "菜单管理-删除", businessType = BusinessTypeEnum.DELETE)
    @DeleteMapping("/{menuId}")
    public Response remove(@PathVariable("menuId") String menuId) throws IllegalAccessException {
        if (sysMenuService.hasChildByMenuId(menuId)) {
            return Response.fail("存在子菜单,不允许删除");
        }
        if (sysMenuService.checkMenuExistRole(menuId)) {
            return Response.fail("菜单已分配,不允许删除");
        }
        sysMenuService.remove(menuId);
        return Response.success();
    }

    /**
     * 获取菜单列表
     *
     */
    @PreAuthorize("@ss.hasPermission('system:menu:list')")
    @GetMapping("/list")
    public Response<List<SysMenuVO>> list(SysMenuVO sysMenuVO) throws IllegalAccessException {
        SysMenuDTO sysMenuDTO = new SysMenuDTO();
        BeanUtils.copyProperties(sysMenuVO, sysMenuDTO);
        return Response.success(JsonUtils.copyList(sysMenuService.listMenu(sysMenuDTO), SysMenuVO.class));
    }

    /**
     * 根据菜单编号获取详细信息
     */
    @PreAuthorize("@ss.hasPermission('system:menu:query')")
    @GetMapping(value = "/{menuId}")
    public Response<SysMenuVO> getInfo(@PathVariable String menuId) {
        SysMenuVO sysMenuVO = new SysMenuVO();
        BeanUtils.copyProperties(sysMenuService.get(menuId), sysMenuVO);
        return Response.success(sysMenuVO);
    }

    /**
     * 获取菜单下拉树列表
     */
    @GetMapping("/treeSelect")
    public Response<List<MenuTreeSelectVO>> treeSelect(SysMenuVO sysMenuVO) throws IllegalAccessException {
        SysMenuDTO sysMenuDTO = new SysMenuDTO();
        BeanUtils.copyProperties(sysMenuVO, sysMenuDTO);
        List<SysMenuDTO> menus = sysMenuService.listMenu(sysMenuDTO);
        return Response.success(buildMenuTreeSelect(menus));
    }

    /**
     * 加载对应角色菜单列表树
     */
    @GetMapping(value = "/roleMenuTreeSelect/{roleId}")
    public Response<MenuRoleTreeSelectVO> roleMenuTreeSelect(@PathVariable("roleId") String roleId) throws IllegalAccessException {
        MenuRoleTreeSelectVO menuRoleTreeSelectVO = new MenuRoleTreeSelectVO();
        menuRoleTreeSelectVO.setCheckedKeys(sysMenuService.listMenuByRoleId(roleId));
        menuRoleTreeSelectVO.setMenus(buildMenuTreeSelect(sysMenuService.listMenu(new SysMenuDTO())));
        return Response.success(menuRoleTreeSelectVO);
    }

    private List<MenuTreeSelectVO> buildMenuTreeSelect(List<SysMenuDTO> menus)
    {
        List<SysMenuVO> menuTrees = JsonUtils.copyList(buildMenuTree(menus), SysMenuVO.class);
        return menuTrees.stream().map(MenuTreeSelectVO::new).collect(Collectors.toList());
    }
    /**
     * 构建前端所需要树结构
     *
     * @param menus 菜单列表
     * @return 树结构列表
     */
    private List<SysMenuDTO> buildMenuTree(List<SysMenuDTO> menus)
    {
        List<SysMenuDTO> returnList = new ArrayList<>();
        List<String> tempList = new ArrayList<>();
        for (SysMenuDTO menu : menus)
        {
            tempList.add(menu.getMenuId());
        }
        for (SysMenuDTO menu : menus) {
            // 如果是顶级节点, 遍历该父节点的所有子节点
            if (!tempList.contains(menu.getParentId())) {
                recursionFn(menus, menu);
                returnList.add(menu);
            }
        }
        if (returnList.isEmpty())
        {
            returnList = menus;
        }
        return returnList;
    }

    /**
     * 递归列表
     *
     * @param list 菜单列表
     * @param t 菜单对象
     */
    private void recursionFn(List<SysMenuDTO> list, SysMenuDTO t)
    {
        // 得到子节点列表
        List<SysMenuDTO> childList = getChildList(list, t);
        t.setChildren(childList);
        for (SysMenuDTO tChild : childList)
        {
            if (hasChild(list, tChild))
            {
                recursionFn(list, tChild);
            }
        }
    }
    /**
     * 得到子节点列表
     */
    private List<SysMenuDTO> getChildList(List<SysMenuDTO> list, SysMenuDTO t)
    {
        List<SysMenuDTO> listNew = new ArrayList<>();
        for (SysMenuDTO n : list) {
            if (n.getParentId().equals(t.getMenuId())) {
                listNew.add(n);
            }
        }
        return listNew;
    }

    /**
     * 判断是否有子节点
     */
    private boolean hasChild(List<SysMenuDTO> list, SysMenuDTO t)
    {
        return getChildList(list, t).size() > 0;
    }
}